home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 9 / AMUG BBS in a Box Volume IX (August 1993) (MacWizards).iso / Files / Prog / B-C / C++Source Code Fmtr.cpt / Src / Formatting.cp / Formatting.cp
Encoding:
Text File  |  1992-04-28  |  28.7 KB  |  1,258 lines  |  [TEXT/MPS ]

  1. #ifndef __CDENT__
  2. #include "cdent.h"
  3. #endif
  4.  
  5. #ifndef __CSCANNER__
  6. #include "CScanner.h"
  7. #endif
  8.  
  9. #ifndef __FORMATLOG__
  10. #include "FormatLog.h"
  11. #endif
  12.  
  13. #ifndef __FORMATTING__
  14. #include "Formatting.h"
  15. #endif
  16.  
  17. #ifndef __PARSERACTIONS__
  18. #include "ParserActions.h"
  19. #endif
  20.  
  21. #ifndef __CTYPE__
  22. #include <ctype.h>
  23. #endif
  24.  
  25. #ifndef __MEMORY__
  26. #include <memory.h>
  27. #endif
  28.  
  29.  
  30. /*
  31. ** How to squeeze when lines exceed the output line length.  The normal case
  32. ** is kSqueezeNone, which means that data are output exactly as they are seen
  33. ** without any other editorializing.  The first other case is kSqueezeBlanks,
  34. ** which removes optional blanks.  If this still doesn't scrunch the source,
  35. ** the final mechanism of kSqueezeConditionalNewLine is called into play, which
  36. ** interprets optional newlines as being mandatory newlines.
  37. */
  38. enum {
  39.     kSqueezeNone,                                // Keep all formatting information
  40.     kSqueezeBlanks,                                // Remove optional blanks
  41.     kSqueezeUseSourceNewLines,                    // Use the original lines in the source
  42.     kSqueezeConditionalNewLine,                    // Use conditional newlines
  43.  
  44.     kSqueezeEnd,                                // Last value, not used
  45.     kSqueezeFirst = kSqueezeBlanks                // First value to try squeezing
  46. };
  47.  
  48.  
  49. //ƒ-
  50. const short        kDfltIndentDelta        =   4;
  51. const short        kDfltWidth                =   0;
  52. const short        kDfltIndent                =   0;
  53. const short        kDfltTempIndent            =   2;
  54. const short        kDfltTabSize            =   4;
  55. const short        kDfltCommentColumn        =  48;
  56. const short        kDfltLineLength            = 120;
  57. const Boolean    kDfltReformatComments    = false;
  58. const Boolean    kDfltSaveSourceNewLines    = false;
  59. const Boolean    kDfltPassConsecutiveNewLines = true;
  60. //ƒ+
  61.  
  62.  
  63. /*
  64. ** Static class variables
  65. */
  66. //ƒ-
  67. DFile*     Formatting::gOutput    = 0;    
  68. FormatLog* Formatting::gFormatLog = 0;
  69. short      Formatting::gTabSize        = kDfltTabSize;
  70. short      Formatting::gLineLength     = kDfltLineLength;
  71. short      Formatting::gCommentColumn  = kDfltCommentColumn;
  72.  
  73. Boolean    Formatting::gDebug         = false;
  74.  
  75. Boolean    Formatting::gReformatComments        = kDfltReformatComments;
  76. Boolean    Formatting::gPassSourceNewLines      = kDfltSaveSourceNewLines;
  77. Boolean    Formatting::gPassConsecutiveNewLines = kDfltPassConsecutiveNewLines;
  78.     
  79. Boolean    Formatting::gFromSource    = true;
  80. short      Formatting::gSourceNewLines = 0;
  81. Boolean    Formatting::gLastNewLineFromSource = true;
  82. //ƒ+
  83.  
  84.  
  85. //ƒ-
  86. struct RollbackVars Formatting::gV = {kSLex_NewLine, 0, true};
  87. struct RollbackVars Formatting::gC;
  88.  
  89. size_t Formatting::gOutputOffset = 0;
  90.  
  91. static Boolean      gConditionalNeedNewLine = false;
  92. static FormatString gConditionalNewLineGlue = 0;
  93. //ƒ+
  94.  
  95.  
  96. //µ   Formatting::IFormatting
  97. #pragma segment Formatting
  98. short Formatting::IFormatting()
  99. {
  100.     short err = fSavedContexts.IDataArea();
  101.  
  102.     if (err != noErr)
  103.         return (err);
  104.  
  105.     SetComma(gFS_expr1);
  106.     SetSemi(gFS_stmt1);
  107.     SetLCurly(gFS_block1);
  108.     SetRCurly(gFS_block2);
  109.     SetLParen(gFS_expr5);
  110.     SetRParen(gFS_expr7);
  111.     SetName(gFS_name1);
  112.     SetOperator(gFS_expr4);
  113.     SetAssign(gFS_expr6);
  114.  
  115.     SetRegister(kRegIndentDelta, kDfltIndentDelta);
  116.     SetRegister(kRegWidth, kDfltWidth);
  117.     SetRegister(kRegIndent, kDfltIndent);
  118.     SetRegister(kRegCurrentColumn, 0);
  119.     SetRegister(kRegTempIndent, kDfltTempIndent);
  120.  
  121.     fContext.fDeclWidth = 0;                    // No minimum width for declarations
  122.     fContext.fDeclLeft = false;                    // "*" and "&" go right
  123.     SetWhatToSqueeze(kSqueezeNone);                // Don't squeeze anything
  124.  
  125.     // Safety measure.  Drop a small context as the first one
  126.     fSavedContexts.Write(&fContext, sizeof(fContext));
  127.     fSavedContexts.DecrCursor(sizeof(fContext));
  128.  
  129.     return (noErr);
  130. }
  131.  
  132.  
  133.  
  134. // µ   Formatting::IFormatting
  135. #pragma segment Formatting
  136. short Formatting::IFormatting(const Formatting *aFormat)
  137. {
  138.     BlockMove(aFormat, this, sizeof(this));
  139.     return (fSavedContexts.IDataArea(&aFormat->fSavedContexts));
  140. }
  141.  
  142.  
  143.  
  144. //µ   Formatting::SetFormatLog
  145. #pragma segment Formatting
  146. void Formatting::SetFormatLog(FormatLog *aFormatLog)
  147. {
  148.     gFormatLog = aFormatLog;
  149.     gFormatLog->Checkpoint(&fContext, &fSavedContexts, fSavedContexts.GetIndex(sizeof(fContext)));
  150.     gC = gV;
  151.     gOutputOffset = gOutput->GetCursor();
  152. }
  153.  
  154.  
  155.  
  156.  
  157. /*
  158. ** Make sure we are at the beginning of a new line
  159. ** Postcondition: LogicalNewLine()
  160. */
  161. //µ   Formatting::FreshLine
  162. #pragma segment Formatting
  163. void Formatting::FreshLine()
  164. {
  165.     if (!IsBOL())
  166.         NewLine();
  167.     else
  168.         LogicalNewLine();
  169. }
  170.  
  171.  
  172.  
  173. /*
  174. ** Move the cursor to the given column.  If the cursor is past the column,
  175. ** leave the cursor where it is unless whitespace is requested.
  176. */
  177. //µ   Formatting::IndentTo
  178. #pragma segment Formatting
  179. void Formatting::IndentTo(short aColumn)
  180. {
  181.     if (CurrentColumn() < aColumn) {
  182.         CurrentColumn() -= CurrentColumn() % gTabSize;// Make tabs work right
  183.         while (CurrentColumn() + gTabSize <= aColumn) {
  184.             gOutput->Putc('\t');
  185.             CurrentColumn() += gTabSize;
  186.         }
  187.  
  188.         while (CurrentColumn() < aColumn) {
  189.             gOutput->Putc(' ');
  190.             CurrentColumn()++;
  191.         }
  192.  
  193.         if (!IsBOL())
  194.             LogicalSpace();
  195.     }
  196. }
  197.  
  198.  
  199.  
  200. /*
  201. ** Move the cursor to the indent column on a fresh line.
  202. */
  203. //µ   Formatting::IndentLine
  204. #pragma segment Formatting
  205. void Formatting::IndentLine(short aColumn)
  206. {
  207.     FreshLine();
  208.     IndentTo(aColumn);
  209. }
  210.  
  211.  
  212.  
  213. /*
  214. ** Format the comment as either an end of line type comment or as a line
  215. ** or block comment at the current indent level
  216. */
  217. //µ   Formatting::Comment
  218. #pragma segment Formatting
  219. void Formatting::Comment(const char *start, const char *end)
  220. {
  221.     short commentColumn = (CurrentColumn() == 0) ? gV.fIndent : gCommentColumn;
  222.  
  223.     /* Advance to the comment column */
  224.     IndentTo(commentColumn);
  225.  
  226.     /*
  227.     ** Comments to the end of line are not wrapped, they are just written
  228.     ** as is.  The comment will not be terminated by a newline: the newline
  229.     ** appears as the next token
  230.     */
  231.     if (start[1] == '/') {
  232.         _Write(start, end - start);
  233.         NeedNewLine(1);
  234.         return;
  235.     }
  236.  
  237.     /*
  238.     ** The comment is a standard C comment.  Advance past the leading comment
  239.     ** start and then display the comment.  We save/restore gFromSource around
  240.     ** these calls as the newlines that are desired should not flush buffers.
  241.     */
  242.     Boolean saveFromSource = gFromSource;
  243.     gFromSource = false;
  244.  
  245.     start += 2;
  246.     _Write("/*", 2);
  247.     if (true || !gReformatComments) {
  248.         Boolean twoStar = false;                // lines prefaced by "**"
  249.         Boolean oneStar = false;                // lines prefaced by " *"
  250.  
  251.         while (start < end) {
  252.             switch (*start) {
  253.             case '\n':
  254.                 // Advance to the first non-blank on the line.  If there are
  255.                 // any intervening newlines, emit them immediately.
  256.                 start++;
  257.                 while (isspace(*start)) {
  258.                     if (*start == '\n')
  259.                         NewLine();
  260.                     start++;
  261.                 }
  262.  
  263.                 // Indent to the comment column
  264.                 NewLine();
  265.                 IndentTo(commentColumn);
  266.  
  267.                 // Indent the body of the line.  If the line starts with
  268.                 // "**", no extra spaces are required.  If the line
  269.                 // starts with "*", then one extra space is required.
  270.                 // If the line is the last line of a comment, then either
  271.                 // zero or one extra space is required: zero when the
  272.                 // previous lines started with zero or two "*", one when
  273.                 // the previous lines began with a single star.
  274.                 if (start[0] != '*') {
  275.                     oneStar = false;
  276.                     twoStar = false;
  277.                     _Write("  ", 2);
  278.                 } else if (start[1] == '*') {
  279.                     oneStar = false;
  280.                     twoStar = true;
  281.                 } else {
  282.                     if (start[1] != '/' || oneStar)
  283.                         _Putc(' ');
  284.                     oneStar = true;
  285.                     twoStar = false;
  286.                 }
  287.                 _Putc(*start);
  288.                 break;
  289.  
  290.             default:
  291.                 _Putc(*start);
  292.                 break;
  293.             }
  294.  
  295.             start++;
  296.         }
  297.     } else {
  298.         // Reformat the comment.  Eventually
  299.     }
  300.  
  301.     // Restore the saved value of gFromSource.
  302.     gFromSource = saveFromSource;
  303.  
  304.     // Indicate that this was a newline
  305.     gV.fLastTokenType = kSLex_Comment;
  306. }
  307.  
  308.  
  309.  
  310. /*
  311. ** Specify the glue to use for the next calls to display.  The glue is
  312. ** immediately interpreted until the "•" character, which requires an item,
  313. ** is scanned.  Because we are "greedy", there is no format left to
  314. ** consume so that only assignment is necessary.
  315. */
  316. //µ   Formatting::SetGlue
  317. #pragma segment Formatting
  318. void Formatting::SetGlue(FormatString aGlue)
  319. {
  320.     gFormatLog->Record(Formatting::SetGlue, aGlue);
  321.     gV.fFormatString = aGlue;
  322.     gV.fGlueString = Interpret(aGlue);
  323.     if (gV.fGlueString == 0)
  324.         diag(kFatal, "Bad formatting string %s\n", aGlue);
  325. }
  326.  
  327.  
  328.  
  329. /*
  330. ** Execute the glue without disturbing any other glue which might
  331. ** be in place.
  332. */
  333. //µ   Formatting::ExecuteGlue
  334. #pragma segment Formatting
  335. void Formatting::ExecuteGlue(FormatString aGlue)
  336. {
  337.     gFormatLog->Record(Formatting::ExecuteGlue, aGlue);
  338.     if (Interpret(aGlue) == 0)
  339.         diag(kFatal, "Bad formatting string %s\n", aGlue);
  340. }
  341.  
  342.  
  343.  
  344. /*
  345. ** Display the token.  Remember what has been displayed and insert spaces as
  346. ** required
  347. */
  348. //µ   Formatting::Display
  349. #pragma segment Formatting
  350. void Formatting::Display(Syntactic *aToken)
  351. {
  352.     Boolean tokenWritten = false;
  353.  
  354.     if (aToken == 0)
  355.         return;
  356.  
  357.     if (gDebug)
  358.         diag(kDebug, "  # Display(%s)\n", Parser::NameOf(aToken->Type()));
  359.  
  360.     switch (aToken->Type()) {
  361.     case kSLex_NewLine:
  362.     case kSPrs_NewLine:
  363.         {
  364.             Boolean emitNewLine = false;
  365.  
  366.             // If the last newline emitted was a source newline and the current
  367.             // output line is empty, then this newline will be emitted.  This
  368.             // lets us preserve line spacing that the user has provided.
  369.             if (gLastNewLineFromSource && CurrentColumn() == 0)
  370.                 emitNewLine = true;
  371.  
  372.             // If we always pass source newlines, then pass this one along also
  373.             if (gPassSourceNewLines)
  374.                 emitNewLine = true;
  375.  
  376.             // If a newline is needed, then this newline fits the bill so it
  377.             // should be emitted.
  378.             if (NewLineNeeded())
  379.                 emitNewLine = true;
  380.  
  381.             // If the last item emitted was a comment, then this newline should
  382.             // be emitted as it is most likely a newline at the end of a comment
  383.             if (gV.fLastTokenType == kSLex_Comment)
  384.                 emitNewLine = true;
  385.  
  386.             // Check if this is the second in a series of newlines.  If it is, then
  387.             // we definitely want to emit this and the previous newline.  Otherwise,
  388.             // remember that this newline is pending if this newline is not emitted
  389.             // this time around
  390.             if (gSourceNewLines > 0) {
  391.                 tokenWritten = aToken->Display(this);
  392.                 emitNewLine = true;
  393.             } else if (!emitNewLine && gPassConsecutiveNewLines)
  394.                 gSourceNewLines = 1;
  395.  
  396.             if (emitNewLine) {
  397.                 tokenWritten = aToken->Display(this);
  398.                 gLastNewLineFromSource = gFromSource;
  399.                 gSourceNewLines = 0;
  400.             } else if (gFromSource) {
  401.                 // If the newline is not being used, remember that it was here in
  402.                 // case we have to reformat the line
  403.                 gFormatLog->Record(Formatting::UseSourceNewLine);
  404.             }
  405.  
  406.             // Remember that this token was written
  407.             if (tokenWritten)
  408.                 gV.fLastTokenType = aToken->Type();
  409.         }
  410.         return;
  411.  
  412.     case kSLex_Comment:
  413.     case kSLex_PoundLine:
  414.         // Comments are special.  They are allowed to ignore the gV.fWantNewLine
  415.         // and gV.fWantSpace flags.  However, gV.fNeedNewLine must be respected, along
  416.         // with emitting any pending newlines.
  417.         if (gSourceNewLines > 0) {
  418.             NewLine();
  419.             gSourceNewLines = 0;
  420.         }
  421.         while (NewLineNeeded())
  422.             NewLine();
  423.  
  424.         tokenWritten = aToken->Display(this);
  425.  
  426.         // Handle a preprocessor line.  The MinorType() of the token describes
  427.         // which type of preprocessor line it is.
  428.         if (aToken->Type() == kSLex_PoundLine)
  429.             if (aToken->MinorType() == kSLex_PoundEndIf)
  430.                 NeedNewLine(2);
  431.             else
  432.                 NeedNewLine(1);
  433.  
  434.         // Remember the type of this token if it was written
  435.         if (tokenWritten) {
  436.             gV.fLastTokenType = aToken->Type();
  437.             gFormatLog->Record(Formatting::Display, aToken);
  438.         }
  439.         return;
  440.  
  441.     case kSLex_EOF:
  442.         // Emit any deferred newlines, both those from the source and those
  443.         // that were requested but not emitted
  444.         if (gSourceNewLines > 0) {
  445.             NewLine();
  446.             gSourceNewLines = 0;
  447.         }
  448.         while (NewLineWanted() || NewLineNeeded())
  449.             NewLine();
  450.         return;
  451.     }
  452.  
  453.  
  454.     /*
  455.     ** Make sure required newlines and spaces have been emitted.  Indent to
  456.     ** the requested column.  Display the token.  Reset the requested indent.
  457.     ** Update gV.fLastTokenType.  Then if the glue was expecting a text string,
  458.     ** interpret the remainder of the glue, advancing it.  Note that this might
  459.     ** modify gV.fLastTokenType if the glue contains a "!n" or "!s" within it.
  460.     ** gSourceNewLines is set to 0 to indicate that any pending newlines were
  461.     ** not emitted and have been ignored.
  462.     */
  463.     CheckWanted();
  464.     IndentTo(gV.fIndent);
  465.     if (gV.fCurDeclStart < 0)
  466.         gV.fCurDeclStart = gV.fIndent;
  467.     tokenWritten = aToken->Display(this);
  468.  
  469.     // Record this transaction in the FormatLog.  Also remember the
  470.     // last token type for future adjacency checks
  471.     if (tokenWritten) {
  472.         gFormatLog->Record(Formatting::Display, aToken);
  473.  
  474.         gV.fIndent = GetIndent();
  475.         gSourceNewLines = 0;
  476.  
  477.         gV.fLastTokenType = aToken->Type();
  478.         if (gV.fLastTokenType == kSLex_Op) {
  479.             gV.fLastTokenType = aToken->MinorType();
  480.             if (gV.fLastTokenType == 0)
  481.                 gV.fLastTokenType = kSLex_Op;
  482.         }
  483.  
  484.         if (gV.fGlueString && *gV.fGlueString == (unsigned char)'•') {
  485.             gV.fGlueString = Interpret(++gV.fGlueString);
  486.             if (gV.fGlueString == 0)
  487.                 diag(kFatal, "Bad formatting string %s\n", gV.fFormatString);
  488.         }
  489.     }
  490. }
  491.  
  492.  
  493.  
  494. //µ   Formatting::DeclPadStart
  495. #pragma segment Formatting
  496. void Formatting::DeclPadStart(int nOperators)
  497. {
  498.     // If declaration operators go left, then there are no pad characters
  499.     if (DeclLeft())
  500.         return;
  501.  
  502.     // Want at least one space separating the operators from the type
  503.     WantSpace();
  504.  
  505.     // Compute the number of pad characters required.
  506.     if (WhatToSqueeze() != kSqueezeBlanks) {
  507.         short nPadChars = CurrentColumn() - gV.fCurDeclStart + nOperators;
  508.         while (nPadChars++ < fContext.fDeclWidth)
  509.             WantSpace();
  510.     }
  511. }
  512.  
  513.  
  514.  
  515. //µ   Formatting::DeclPadEnd
  516. #pragma segment Formatting
  517. void Formatting::DeclPadEnd()
  518. {
  519.     // If declaration operators go right, then there are no pad characters
  520.     if (!DeclLeft())
  521.         return;
  522.  
  523.     // Want at least one space separating the operators from the type
  524.     WantSpace();
  525.  
  526.     // Compute the number of pad characters required.
  527.     if (WhatToSqueeze() != kSqueezeBlanks) {
  528.         short nPadChars = CurrentColumn() - gV.fCurDeclStart;
  529.         while (nPadChars++ < fContext.fDeclWidth)
  530.             WantSpace();
  531.     }
  532. }
  533.  
  534.  
  535.  
  536. //µ   Formatting::DeclWidth
  537. #pragma segment Formatting
  538. void Formatting::DeclWidth(Boolean goLeft, int aWidth)
  539. {
  540.     fContext.fDeclLeft = goLeft;
  541.     fContext.fDeclWidth = aWidth;
  542.     gV.fCurDeclStart = -1;
  543. }
  544.  
  545.  
  546.  
  547.  
  548. /*
  549. ** Low level display routines
  550. */
  551.  
  552. //µ   Formatting::Print
  553. #pragma segment Formatting
  554. void Formatting::Print(const char *aString)
  555. {
  556.     if (aString)
  557.         Write(aString, strlen(aString));
  558. }
  559.  
  560.  
  561.  
  562. //µ   Formatting::Puts
  563. #pragma segment Formatting
  564. void Formatting::Puts(const char *aString)
  565. {
  566.     gOutput->Puts(aString);
  567.     LogicalNewLine();
  568. }
  569.  
  570.  
  571. //µ   Formatting::Putc
  572. #pragma segment Formatting
  573. void Formatting::Putc(int aChar)
  574. {
  575.     if (aChar == '\n')
  576.         NewLine();
  577.     else {
  578.         _Putc(aChar);
  579.         if (isspace(aChar))
  580.             LogicalSpace();
  581.     }
  582. }
  583.  
  584.  
  585. //µ   Formatting::NewLine
  586. #pragma segment Formatting
  587. void Formatting::NewLine()
  588. {
  589.     // Check if the LineLength() has been exceeded.  If it has, initiate
  590.     // the line shortening procedures.  (This is a test, it is only a test)
  591.  
  592.     if (gDebug)
  593.         diag(kDebug, "  # \\n\n");
  594.  
  595.     // Emit the newline to standard out.  Note that a newline has been emitted.
  596.     // Indicate that the last newline was not from source.  The line has been
  597.     // emitted, so reset
  598.     gOutput->Putc('\n');
  599.     LogicalNewLine();
  600.     gLastNewLineFromSource = false;
  601.  
  602.     // Checkpoint the world if required.  Flush the buffer if the newline came
  603.     // from source.  We don't flush when not coming from source because the
  604.     // newline *might* be provisional and undone when retrying.
  605.     if (gFromSource) {
  606.         gOutput->Flush(4096);
  607.         gFormatLog->Checkpoint(&fContext, &fSavedContexts, fSavedContexts.GetIndex(sizeof(fContext)));
  608.         gC = gV;
  609.         gOutputOffset = gOutput->GetCursor();
  610.     }
  611. }
  612.  
  613.  
  614. //µ   Formatting::Write
  615. #pragma segment Formatting
  616. void Formatting::Write(const void *aString, size_t n)
  617. {
  618.     gOutput->Write(aString, n);
  619.  
  620.     const char *p = (const char *)aString;
  621.  
  622.     while (n--) {
  623.         char aChar = *p++;
  624.  
  625.         if (aChar == '\n')
  626.             LogicalNewLine();
  627.         else if (isspace(aChar))
  628.             LogicalSpace();
  629.         else
  630.             CurrentColumn()++;
  631.     }
  632. }
  633.  
  634.  
  635.  
  636. //µ   Formatting::LogicalSpace
  637. #pragma segment Formatting
  638. void Formatting::LogicalSpace()
  639. {
  640.     if (SpaceWanted())
  641.         --gV.fWantSpace;
  642.     if (SpaceNeeded())
  643.         --gV.fNeedSpace;
  644.     gV.fLastTokenType = kSLex_Null;
  645. }
  646.  
  647.  
  648.  
  649. //µ   Formatting::LogicalNewLine
  650. #pragma segment Formatting
  651. void Formatting::LogicalNewLine()
  652. {
  653.     CurrentColumn() = 0;
  654.     if (NewLineWanted())
  655.         --gV.fWantNewLine;
  656.     if (NewLineNeeded())
  657.         --gV.fNeedNewLine;
  658.     gV.fWantSpace = 0;
  659.     gV.fNeedSpace = 0;
  660.     AtBOL();
  661. }
  662.  
  663.  
  664.  
  665. //µ   Formatting::OpenContext
  666. #pragma segment Formatting
  667. void Formatting::OpenContext(Boolean isGroup)
  668. {
  669.     int aDepth = fSavedContexts.GetIndex(sizeof(fContext));
  670.  
  671.     gFormatLog->RecordDepth(aDepth);
  672.     gFormatLog->Record(Formatting::OpenContext, isGroup);
  673.  
  674.     fSavedContexts.Write(&fContext, sizeof(fContext));
  675.     fContext.fIsGroup = isGroup;
  676.     if (gDebug)
  677.         diag(kDebug, "  # OpenContext(%d),  SP = %d\n", isGroup, aDepth);
  678. }
  679.  
  680.  
  681.  
  682. //µ   Formatting::CloseContext
  683. #pragma segment Formatting
  684. void Formatting::CloseContext()
  685. {
  686.     int aDepth = fSavedContexts.GetIndex(sizeof(fContext)) - 1;
  687.  
  688.     gFormatLog->RecordDepth(aDepth);
  689.     gFormatLog->Record(Formatting::CloseContext);
  690.  
  691.     // Restore the previous context.  Preserve the current column as that does
  692.     // is a dynamic value.
  693.     short aColumn = CurrentColumn();
  694.  
  695.     fSavedContexts.DecrCursor(sizeof(fContext));
  696.     fContext = *(FContext *)fSavedContexts.GetData();
  697.  
  698.     CurrentColumn() = aColumn;
  699.     gV.fIndent = GetIndent();
  700.     if (gDebug)
  701.         diag(kDebug, "  # CloseContext, SP = %d\n", aDepth);
  702. }
  703.  
  704.  
  705.  
  706. //µ   Formatting::RestoreIndent
  707. #pragma segment Formatting
  708. void Formatting::RestoreIndent()
  709. {
  710.     gFormatLog->Record(Formatting::RestoreIndent);
  711.  
  712.     FContext * aContext = (FContext *)fSavedContexts.GetData(fSavedContexts.GetCursor() - sizeof(fContext));
  713.  
  714.     fContext.fReg[kRegIndentDelta] = aContext->fReg[kRegIndentDelta];
  715.     fContext.fReg[kRegWidth] = aContext->fReg[kRegWidth];
  716.     fContext.fReg[kRegIndent] = aContext->fReg[kRegIndent];
  717.     fContext.fReg[kRegTempIndent] = aContext->fReg[kRegTempIndent];
  718.  
  719.     gV.fIndent = GetIndent();
  720. }
  721.  
  722.  
  723.  
  724. /*
  725. ** Postcondition: LogicalSpace() || LogicalNewLine()
  726. */
  727. //µ   Formatting::CheckWanted
  728. #pragma segment Formatting
  729. void Formatting::CheckWanted()
  730. {
  731.     // Do newlines first.  This can turn off the need for spaces
  732.     while (NewLineWanted() || NewLineNeeded())
  733.         NewLine();
  734.  
  735.     // Emit spaces after newlines.
  736.     while (SpaceWanted() || SpaceNeeded()) {
  737.         _Putc(' ');
  738.         LogicalSpace();
  739.     }
  740.  
  741.     // Finally check if we need a newline.
  742.     if (CurrentColumn() >= LineLength()) {
  743.         // If we are in the middle of a Rollback(), then don't Rollback() again.
  744.         if (gFromSource) {
  745.             // Indicate that text is no longer from source.
  746.             gFromSource = false;
  747.  
  748.             // Iterate over the squeeze options that have not been applied
  749.             // until the current column is less than the line length at the
  750.             // end of the squeeze.  Note that the squeeze option is propagated
  751.             // up the context stack
  752.             short sqType = WhatToSqueeze();
  753.  
  754.             // Squeeze from the first choice to the last
  755.             if (sqType < kSqueezeFirst)
  756.                 sqType = kSqueezeFirst;
  757.  
  758.             while (sqType < kSqueezeEnd && CurrentColumn() > LineLength()) {
  759.                 if (gDebug)
  760.                     diag(kDebug, "  #\n  # ——> Rollback starting.  SetSqueeze(%d)\n", sqType);
  761.  
  762.                 // Rollback the log, the global variables, and the beginning of
  763.                 // the line
  764.                 gFormatLog->Rollback(&fContext, &fSavedContexts);
  765.                 gV = gC;
  766.                 gOutput->SetCursor(gOutputOffset);
  767.  
  768.                 // Start squeezing.
  769.                 SetSqueeze(sqType);
  770.  
  771.                 // Redo all the saved items
  772.                 while (gFormatLog->Redo(this))
  773.                     ;
  774.  
  775.                 if (gDebug)
  776.                     diag(kDebug, "  # <—— Rollback completed\n  #\n");
  777.  
  778.                 sqType++;
  779.             }
  780.  
  781.             // Done redoing.  Allow redo to work.
  782.             SetSqueeze(kSqueezeNone);
  783.             gFormatLog->EnableRecord();
  784.             gFromSource = true;
  785.  
  786.             // If still beyond the end of the line, just put out a newline
  787.             if (CurrentColumn() >= LineLength())
  788.                 NewLine();
  789.         }
  790.     }
  791. }
  792.  
  793.  
  794. //µ   Formatting::SetSqueeze
  795. #pragma segment Formatting
  796. void Formatting::SetSqueeze(short whatToSqueeze)
  797. {
  798.     int minDepth = gFormatLog->MinDepth();
  799.     int maxDepth = gFormatLog->MaxDepth();
  800.  
  801.     SetWhatToSqueeze(whatToSqueeze);
  802.     while (minDepth <= maxDepth) {
  803.         ((FContext *)fSavedContexts.GetData(minDepth, sizeof(fContext)))->fWhatToSqueeze = whatToSqueeze;
  804.         ++minDepth;
  805.     }
  806.  
  807.     gConditionalNeedNewLine = (whatToSqueeze == kSqueezeConditionalNewLine);
  808.     gConditionalNewLineGlue = 0;
  809. }
  810.  
  811.  
  812. //µ   Formatting::UseSourceNewLine
  813. #pragma segment Formatting
  814. void Formatting::UseSourceNewLine()
  815. {
  816.     if (WhatToSqueeze() == kSqueezeUseSourceNewLines)
  817.         NeedNewLine();
  818. }
  819.  
  820.  
  821.  
  822. /*µ - FormatString interpreters
  823. */
  824.  
  825.  
  826. /*µ   consumeWhiteSpace
  827. ** Return a pointer to the first non-whitespace character in the FormatString
  828. */
  829. static FormatString consumeWhiteSpace(register FormatString aFormat)
  830. {
  831.     if (aFormat)
  832.         while (isspace(*aFormat))
  833.             ++aFormat;
  834.  
  835.     return (aFormat);
  836. }
  837.  
  838.  
  839. /*µ   consumeBalancedParens
  840. **    Assure aFormat points at an open parenthesis, then advance it past
  841. ** all balanced pairs of parentheses.  Return 0 if an error occurred,
  842. ** otherwise return the pointer to the first character past the closing
  843. ** parenthesis.
  844. */
  845. static FormatString consumeBalancedParens(register FormatString aFormat)
  846. {
  847.     aFormat = consumeWhiteSpace(aFormat);
  848.  
  849.     if (aFormat && *aFormat == '{') {
  850.         int parenCount = 0;
  851.  
  852.         while (*aFormat) {
  853.             switch (*aFormat++) {
  854.             case '{':
  855.                 ++parenCount;
  856.                 break;
  857.  
  858.             case '}':
  859.                 if (--parenCount == 0)
  860.                     return (consumeWhiteSpace(aFormat));
  861.                 break;
  862.  
  863.             case '\'':
  864.                 aFormat++;
  865.                 break;
  866.             }
  867.         }
  868.     }
  869.  
  870.     return (0);
  871. }
  872.  
  873.  
  874.  
  875. //µ   Formatting::Interpret
  876. #pragma segment Formatting
  877. FormatString Formatting::Interpret(register FormatString aFormat)
  878. {
  879.     short n;
  880.     Boolean aBool;
  881.  
  882.     while (aFormat && *aFormat) {
  883.         switch (*aFormat++) {
  884.         case 'i':                                // Set the indent
  885.             SetRegister(kRegIndent, aFormat);
  886.             gV.fIndent = GetIndent();
  887.             SetRegister(kRegTempIndent, gV.fIndent);
  888.             if (gDebug)
  889.                 diag(kDebug, "  # i%d\n", GetIndent());
  890.             break;
  891.  
  892.         case 't':                                // Set the temporary indent
  893.             SetRegister(kRegTempIndent, aFormat);
  894.             if (gDebug)
  895.                 diag(kDebug, "  # t%d\n", GetRegister(kRegTempIndent));
  896.             break;
  897.  
  898.         case 'c':                                // Set the next column
  899.             gV.fIndent = GetExpr(aFormat);
  900.             if (gDebug)
  901.                 diag(kDebug, "  # c%d\n", gV.fIndent);
  902.             break;
  903.  
  904.         case '/':                                // Conditional break
  905.             {
  906.                 Boolean didNewLine = false;
  907.  
  908.                 if (*aFormat == '+')
  909.                     SetRegister(kRegTempIndent, aFormat);
  910.                 if (gConditionalNeedNewLine && (gConditionalNewLineGlue == 0 || gConditionalNewLineGlue == aFormat)) {
  911.                     didNewLine = true;
  912.                     gConditionalNewLineGlue = aFormat;
  913.                     NeedNewLine();
  914.                 }
  915.                 if (gDebug)
  916.                     diag(kDebug, "  # / (%d)\n", didNewLine);
  917.             }
  918.             break;
  919.  
  920.         case 'd':                                // Declaration width
  921.             aBool = (*aFormat++ == 'l');
  922.             n = GetExpr(aFormat);
  923.             DeclWidth(aBool, n);
  924.             break;
  925.  
  926.         case 's':                                // Emit blanks
  927.             aBool = (*aFormat == '#');
  928.             if (aBool)
  929.                 ++aFormat;
  930.             n = GetExpr(aFormat);
  931.             if (aBool) {
  932.                 if (WhatToSqueeze() != kSqueezeBlanks)
  933.                     WantSpace(n);
  934.             } else
  935.                 NeedSpace(n);
  936.             if (gDebug)
  937.                 diag(kDebug, "  # s%s%d\n", (aBool ? "#" : ""), n);
  938.             break;
  939.  
  940.         case 'n':                                // Want new lines
  941.             aBool = (*aFormat == '#');
  942.             if (aBool)
  943.                 ++aFormat;
  944.             n = GetExpr(aFormat);
  945.             if (aBool)
  946.                 WantNewLine(n);
  947.             else
  948.                 NeedNewLine(n);
  949.             if (gDebug)
  950.                 diag(kDebug, "  # n%s%d\n", (aBool ? "#" : ""), n);
  951.             break;
  952.  
  953.         case '&':                                // Require
  954.             switch (*aFormat++) {
  955.             case 'n':                            // …beginning of line
  956.                 aBool = (!IsBOL() && !NewLineWanted() && !NewLineNeeded());
  957.                 if (aBool)
  958.                     NeedNewLine();
  959.                 if (gDebug)
  960.                     diag(kDebug, "  # &n(%d)\n", aBool);
  961.                 break;
  962.  
  963.             case 's':                            // …last was space
  964.                 aBool = (!IsBOL() && gV.fLastTokenType != kSLex_Null && !SpaceWanted() && !SpaceNeeded());
  965.                 if (aBool)
  966.                     NeedSpace();
  967.                 if (gDebug)
  968.                     diag(kDebug, "  # &s(%d)\n", aBool);
  969.                 break;
  970.  
  971.             default:
  972.                 return (0);
  973.             }
  974.             break;
  975.  
  976.         case '!':                                // Assert…
  977.             switch (*aFormat++) {
  978.             case 'n':                            // …beginning of line
  979.                 if (!NewLineWanted() && !NewLineNeeded())
  980.                     AtBOL();
  981.                 else {
  982.                     if (NewLineWanted())
  983.                         gV.fWantNewLine--;
  984.                     if (NewLineNeeded())
  985.                         gV.fNeedNewLine--;
  986.                 }
  987.  
  988.                 if (gDebug)
  989.                     diag(kDebug, "  # !n\n");
  990.                 break;
  991.  
  992.             case 's':                            // …last was space
  993.                 if (!SpaceWanted() && !SpaceNeeded())
  994.                     gV.fLastTokenType = kSLex_Null;
  995.                 else {
  996.                     if (SpaceWanted())
  997.                         gV.fWantSpace--;
  998.                     if (SpaceNeeded())
  999.                         gV.fNeedSpace--;
  1000.                 }
  1001.  
  1002.                 if (gDebug)
  1003.                     diag(kDebug, "  # !s\n");
  1004.                 break;
  1005.  
  1006.             default:
  1007.                 return (0);
  1008.             }
  1009.             break;
  1010.  
  1011.         case '?':                                // Conditional…
  1012.             switch (*aFormat++) {
  1013.             case 'n':                            // …beginning of line
  1014.                 aBool = IsBOL();
  1015.                 break;
  1016.  
  1017.             case 'i':                            // …last was id or value
  1018.                 aBool = IsIdentifierType(gV.fLastTokenType);
  1019.                 break;
  1020.  
  1021.             case 'o':                            // …last was operator
  1022.                 aBool = IsOperatorType(gV.fLastTokenType);
  1023.                 break;
  1024.  
  1025.             case '\'':
  1026.                 switch (*aFormat++) {
  1027.                 case '(':                        // …last was left paren 
  1028.                     aBool = (gV.fLastTokenType == kSLex_LParen);
  1029.                     break;
  1030.  
  1031.                 case ')':                        // …last was right paren 
  1032.                     aBool = (gV.fLastTokenType == kSLex_RParen);
  1033.                     break;
  1034.  
  1035.                 case '{':                        // …last was left curly 
  1036.                     aBool = (gV.fLastTokenType == kSLex_LCurly);
  1037.                     break;
  1038.  
  1039.                 case '}':                        // …last was right curly 
  1040.                     aBool = (gV.fLastTokenType == kSLex_RCurly);
  1041.                     break;
  1042.  
  1043.                 case ';':                        // …last was operator
  1044.                     aBool = (gV.fLastTokenType == kSLex_SemiColon);
  1045.                     break;
  1046.  
  1047.                 default:                        // …error
  1048.                     return (0);
  1049.                 }
  1050.                 break;
  1051.  
  1052.             default:                            // …error
  1053.                 return (0);
  1054.             }
  1055.  
  1056.             if (gDebug)
  1057.                 diag(kDebug, "  # ?%c == %d\n", aFormat[-1], aBool);
  1058.  
  1059.             if (!aBool)
  1060.                 aFormat = consumeBalancedParens(aFormat);
  1061.             if (aFormat == 0 || *aFormat++ != '{')
  1062.                 return (0);
  1063.             break;
  1064.  
  1065.         case '=':                                // Assign to a register
  1066.             if (!isdigit(*aFormat))
  1067.                 return (0);
  1068.             n = *aFormat++ - '0';
  1069.             SetRegister(n, aFormat);
  1070.             if (gDebug)
  1071.                 diag(kDebug, "  # =%d %d\n", n, GetRegister(n));
  1072.             break;
  1073.  
  1074.         case ((unsigned char)'•'):                // Display an item
  1075.             if (gDebug)
  1076.                 diag(kDebug, "  # •\n");
  1077.             return (aFormat - 1);
  1078.  
  1079.         case ' ':                                // Ignore whitespace
  1080.         case '\t':
  1081.             break;
  1082.  
  1083.         case '{':                                // Ignore it
  1084.             aFormat = consumeBalancedParens(aFormat - 1);
  1085.             break;
  1086.  
  1087.         case '}':                                // Skip over it
  1088.             break;
  1089.  
  1090.         default:                                // Unknown character.
  1091.             return (aFormat - 1);
  1092.         }
  1093.     }
  1094.  
  1095.     return (aFormat);
  1096. }
  1097.  
  1098.  
  1099.  
  1100. /*
  1101. ** Set the register to the value of the expression in the string
  1102. */
  1103. //µ   Formatting::SetRegister
  1104. #pragma segment Formatting
  1105. void Formatting::SetRegister(int aRegister, FormatString &aFormat)
  1106. {
  1107.     int op;
  1108.  
  1109.     switch (*aFormat) {
  1110.     case '+':
  1111.     case '-':
  1112.         op = *aFormat++;
  1113.         break;
  1114.     default:
  1115.         op = '=';
  1116.         break;
  1117.     }
  1118.  
  1119.     int expr = GetExpr(aFormat);
  1120.     if (aFormat != 0) {
  1121.         switch (op) {
  1122.         case '+':
  1123.             SetRegister(aRegister, GetRegister(aRegister) + expr);
  1124.             break;
  1125.  
  1126.         case '-':
  1127.             SetRegister(aRegister, GetRegister(aRegister) - expr);
  1128.             break;
  1129.  
  1130.         case '=':
  1131.             SetRegister(aRegister, expr);
  1132.             break;
  1133.         }
  1134.     }
  1135. }
  1136.  
  1137.  
  1138.  
  1139. /*
  1140. ** Evaluate the expression.  Return the value and advance aFormat past the
  1141. ** expression.  If there was an error, aFormat == 0.  If there was no
  1142. ** expression, return the default value.
  1143. */
  1144. //µ   Formatting::GetExpr
  1145. #pragma segment Formatting
  1146. int Formatting::GetExpr(FormatString &aFormat, int defaultValue)
  1147. {
  1148.     int sum = defaultValue;
  1149.     int op = '=';
  1150.     Boolean done = false;
  1151.  
  1152.     while (!done && aFormat && *aFormat) {
  1153.         int term;
  1154.         Boolean haveTerm = false;
  1155.  
  1156.         switch (*aFormat) {
  1157.         case '+':                                // Add to sum
  1158.         case '-':                                // Subtract from sum
  1159.             // Remember operation, get operand.
  1160.             op = *aFormat++;
  1161.             break;
  1162.  
  1163.         case ((unsigned char)'®'):                // Register reference
  1164.             if (!isdigit(*++aFormat)) {
  1165.                 aFormat = 0;
  1166.                 return (0);
  1167.             }
  1168.             term = GetRegister(*aFormat++ - '0');
  1169.             haveTerm = true;
  1170.             break;
  1171.  
  1172.         case ' ':                                // Ignore whitespace
  1173.         case '\t':
  1174.             ++aFormat;
  1175.             break;
  1176.  
  1177.         default:                                // Number
  1178.             if (isdigit(*aFormat)) {
  1179.                 term = 0;
  1180.                 while (isdigit(*aFormat)) {
  1181.                     term *= 10;
  1182.                     term += *aFormat++ - '0';
  1183.                 }
  1184.                 haveTerm = true;
  1185.             } else
  1186.                 done = true;
  1187.             break;
  1188.         }
  1189.  
  1190.         // Here after having scanned a term.  op is the operation.
  1191.         if (haveTerm)
  1192.             switch (op) {
  1193.             case '=':
  1194.                 sum = term;
  1195.                 break;
  1196.             case '+':
  1197.                 sum += term;
  1198.                 break;
  1199.             case '-':
  1200.                 sum -= term;
  1201.                 break;
  1202.             }
  1203.     }
  1204.  
  1205.     return (sum);
  1206. }
  1207.  
  1208.  
  1209.  
  1210. /*
  1211. **  Return true if the last token was a beginning of line token type
  1212. */
  1213. //µ   Formatting::IsBOL
  1214. #pragma segment Formatting
  1215. Boolean Formatting::IsBOL()
  1216. {
  1217.     return (gV.fLastTokenType == kSLex_NewLine || gV.fLastTokenType == kSPrs_NewLine);
  1218. }
  1219.  
  1220.  
  1221.  
  1222. /*
  1223. ** Return true if the type is one which should be treated as an identifier
  1224. ** for formatting purposes.  This class subsumes reserved words and values
  1225. ** in addition to identifiers as these may not be placed adjacent to each
  1226. ** other with impunity
  1227. */
  1228. //µ   Formatting::IsIdentifierType
  1229. #pragma segment Formatting
  1230. Boolean Formatting::IsIdentifierType(short aType)
  1231. {
  1232.     switch (aType) {
  1233.     case kSLex_Id:
  1234.     case kSLex_ParsedId:
  1235.     case kSLex_Value:
  1236.     case kSPrs_Id:
  1237.     case kSPrs_DeclOperator:
  1238.         return (true);
  1239.  
  1240.     default:
  1241.         return (aType >= kSLex_ReservedWordFirst && aType <= kSLex_ReservedWordLast);
  1242.     }
  1243. }
  1244.  
  1245.  
  1246.  
  1247. /*
  1248. ** Return true if the type is that belonging to an operator
  1249. */
  1250. //µ   Formatting::IsOperatorType
  1251. #pragma segment Formatting
  1252. Boolean Formatting::IsOperatorType(short aType)
  1253. {
  1254.     return (aType >= kSLex_OpFirst && aType <= kSLex_OpLast);
  1255. }
  1256.  
  1257.  
  1258.